<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>身份验证</title>
    <link rel="stylesheet" href="css/index.css">
    <style></style>
</head>

<body>
<div class="app-container">
    <div class="warpper">
        <!-- 提示信息 -->
        <div class="btnBox">
            <img src="./imgs/close.svg" alt="" srcset="">
        </div>
        <p class="tip" id="tipTxt">模型初始化中...</p>
        <div class="photoBox">
            <video onPlay="userface.onPlay(this)" id="myVideo" ref="myVideo" class="pic_video" playsinline autoplay x5-video-player-type="h5"></video>
            <canvas id="overlay" ref="overlay"></canvas>
            <span id="hoverTxt" class="hovertxt"></span>
        </div>
        <div style="display: none;">
            <img :src="imgSrc" alt="" class="imgSrc" id="imgSrc">
        </div>
        <div class="safeBox">
            <img src="./imgs/safe.png" alt="" srcset=""><span>信息已加密,仅用于身份认证</span>
        </div>
    </div>
</div>
<script src="js/jquery2.0.js"></script>
<script src="js/flexible.js"></script>
<!-- 开发环境版本，包含了有帮助的命令行警告 -->
<!--<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>-->
<!-- 生产环境版本，优化了尺寸和速度 -->
<!--<script src="https://cdn.jsdelivr.net/npm/vue"></script>-->
<script src="js/vue.js"></script>
<script src="js/face-api.js"></script>
<script src="js/vconsole.min.js"></script>

<script>
    function moveToCameraAVG() {
        var self = this;
        if (navigator.mediaDevices === undefined) {
            navigator.mediaDevices = {};
        }
        if (navigator.mediaDevices.getUserMedia === undefined) {
            navigator.mediaDevices.getUserMedia = function (constraints) {
                var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
                if (!getUserMedia) {
                    return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
                }
                return new Promise(function (resolve, reject) {
                    getUserMedia.call(navigator, constraints, resolve, reject);
                });
            }
        }
        if (window.stream) {
            window.stream.getTracks().forEach(track => {
                track.stop();
        });
        }
        var constraints = window.constraints = {
            audio: false,
            video: {
                sourceId: 'default',
                facingMode:  'user'
            }
        };
        navigator.mediaDevices.getUserMedia(constraints)
            .then(stream => {
            var video = document.getElementById('myVideo');
        try {
            window.stream = stream;
            video.srcObject = stream;
        } catch (error) {
            video.src = window.URL.createObjectURL(stream);
        }
        self.localMediaStream = stream;
        setTimeout(() => {
            document.getElementById('tipTxt').innerHTML = '请确保正对手机，光线充足'
            userface.setHoverTxt('初始化中...')
        }, 400)
        video.play();
    })
    .catch(function (err) {
            console.info(err.name + ": " + err.message);
        });
    }


    var userface = {
        tipTxt: '模型初始化中...',
        vid: document.querySelector('#myVideo'),
        vid_width: 200,
        vid_height: 200,
        overlay: document.querySelector('#overlay'),
        overlayCC: document.querySelector('#overlay').getContext('2d'),
        palyFlag: false,
        isOpenMouth: false, // 张嘴是否通过
        isTwinkle: false, // 眨眼是否通过
        last_mouth_distance: 0, // 上一次嘴巴张开的高度
        imgSrc: '', // 截图
        mouthNum: 0, // 张嘴次数
        last_nose_distance_y: 0, // 上一次鼻子的垂直坐标
        last_nose_distance_x: 0, // 上一次鼻子的水平坐标
        lastTime: 0, // 时间因素
        last_DIF: 0, // 记录上一次左右两边距离差值
        first_outline_l: [], // 存储每次识别到人脸时左轮廓线中间点距离鼻点的距离
        first_outline_r: [], // 存储每次识别到人脸时右轮廓线中间点距离鼻点的距离
        counter: 0, // 向左或向右的次数,
        last_nose_left: 0,
        last_nose_top: 0,
        last_dis_eye_norse: 0,
        firstfaceImg: '',
        getPhotoNum: 0,//调取getPhotoNum的次数
        // 判断是否可以调起摄像头
        callTheCamera: function (callback) {
            callback()
            $('.app-container').addClass('active')
        },
		load1: function(){
			
			return new Promise((resolve,reject)=>{
				let net = faceapi.nets.tinyFaceDetector
				let oReq = new XMLHttpRequest();
				oReq.open("GET", "models/tiny_face_detector_model.weights", true);        // 加载模型，传入路径
				oReq.responseType = "arraybuffer";        // 设置加载的文件返回类型
				oReq.onload = function () {
								 console.log('加载成功')
				    let arrayBuffer = oReq.response; // 加载完模型文件后获取到对应buffer
				    let weights = new Float32Array(arrayBuffer)
				    net.load(weights).then(()=>{
						console.log('加载成功')
						resolve();
					})
		
							
				} 
				oReq.send(null);
			})
			
		},
		
		load2: function(){
			return new Promise((resolve,reject)=>{
				const net1 = faceapi.nets.faceLandmark68Net
				var oReq2 = new XMLHttpRequest();
				oReq2.open("GET", "models/face_landmark_68_model.weights", true);        // 加载模型，传入路径
				oReq2.responseType = "arraybuffer";        // 设置加载的文件返回类型
				oReq2.onload = function () {
								 console.log('加载成功2')
					var arrayBuffer = oReq2.response; // 加载完模型文件后获取到对应buffer
					const weights = new Float32Array(arrayBuffer)
					net1.load(weights).then(()=>{
						console.log('加载成功2')
						resolve();
					})
								 
				} 
				oReq2.send(null);
			})
		},
        // 初始化加载模型
        initFun() {
            // 加载神经网络模型
            Promise.all([ // 加载我想要使用的模型 同时对应的json文件和shard文件要处在同一目录 不然读取的时候可能读取不到。当你读取不到的时候你可能会报 SyntaxError: Unexpected token < in JSON at position 0。这点略坑
                userface.load1(),
                userface.load2(),
            ]).then(() => {
                moveToCameraAVG();
        }); // 载入成功之后唤醒摄像头
        },
        // 视频流监听
        onPlay: async function (event) {
            var that = this;
            const video = this.vid; const canvas = this.overlay; const vid_width = document.querySelector('#myVideo').videoWidth || 200; const vid_height = document.querySelector('#myVideo').videoHeight || 200;
            if (this.vid.paused || this.vid.ended || !this.palyFlag) {
                setTimeout(() => {
                    userface.onPlay();
                userface.palyFlag = true
            }, 1000);
            }
            const displaySize = { width: vid_width, height: vid_height }; // 这部分的大小是等同video的大小的
            faceapi.matchDimensions(canvas, displaySize); // 声明大小
            const detections = await faceapi
                .detectAllFaces(video, new faceapi.TinyFaceDetectorOptions({ inputSize: 128 }))
                .withFaceLandmarks()
                //.withFaceExpressions();
            const resizedDetections = faceapi.resizeResults(
                detections,
                displaySize
            );
            if (resizedDetections.length == 0) {
                this.setHoverTxt('未检测到人脸')
            } else {
                // 限定人脸在圆框中展示的位置
                if (resizedDetections[0].alignedRect.relativeBox.bottomLeft.x<0.2 || resizedDetections[0].alignedRect.relativeBox.bottomLeft.x > 0.5 ) {
                    this.setHoverTxt('请将脸正对中间')
                }else if(resizedDetections[0].alignedRect.relativeBox.bottomLeft.y >1.5){
                    this.setHoverTxt('距离太近请调整')
                } else {
                    if (this.getPhotoNum == 0) {
                        this.getPhoto();
                    }
                    this.setHoverTxt('请张张嘴巴')
                    const landmarks = resizedDetections[0].landmarks;
                    const landmarkPositions = landmarks.positions
                    // 或者得到各个轮廓的位置，
                    // 仅适用于68点面部标记(FaceLandmarks68)
                    // const jawOutline = landmarks.getJawOutline()
                    // const nose = landmarks.getNose()
                    // const mouth = landmarks.getMouth()
                    // const leftEye = landmarks.getLeftEye()
                    // const rightEye = landmarks.getRightEye()
                    // const leftEyeBbrow = landmarks.getLeftEyeBrow()
                    // const rightEyeBrow = landmarks.getRightEyeBrow()
                    if (!this.isOpenMouth) {
                        // 张张嘴判断
                        this.openMouth(landmarkPositions)
                        // 摇头
                        // this.shakeHead(landmarkPositions)
                        // 眨眼睛
                        // this.twinkle(landmarkPositions)
                    } else if (!this.isTwinkle) {
                        // this.twinkle(landmarkPositions);
                    }
                    faceapi.draw.drawFaceLandmarks(canvas, resizedDetections);
                }
                // faceapi.draw.drawDetections(canvas, resizedDetections);
                // faceapi.draw.drawFaceExpressions(canvas, resizedDetections);
            }
            if (this.isOpenMouth) {
                // 截图
                this.getPhoto();
            } else {
                setTimeout(() => this.onPlay())
            }
        },
        // 张嘴判断
        openMouth: function (positions) {
            // 63:上嘴唇底部中间点 67：下嘴唇顶部中间点
            var mouth_distance = positions[67].y - positions[63].y;
            var nose_distance_y = positions[31].y; var nose_distance_x = positions[31].x;
            this.setHoverTxt('请张张嘴巴')
            this.last_nose_distance_y = nose_distance_y;
            this.last_nose_distance_x = nose_distance_x;
            if (this.last_mouth_distance > 0 && mouth_distance > 0 && (Math.abs(this.last_mouth_distance - mouth_distance) > 6) && (Math.abs(this.last_nose_distance_y - nose_distance_y) < 0.6 && Math.abs(this.last_nose_distance_x - nose_distance_x) < 0.6)) {
                this.mouthNum++;
                if (this.mouthNum > 1) {
                    console.log('张嘴通过')
                    document.querySelector('#tipTxt').innerHTML = '验证中，请稍等...'
                    $('.photoBox').children().hide()
                    this.isOpenMouth = true;
                    this.last_mouth_distance = 0;
                    this.mouthNum = 0;
                }
            }
            this.last_mouth_distance = mouth_distance;
        },
        // 截取视频中符合条件的图片
        getPhoto: function () {
            this.getPhotoNum++;
            this.overlayCC.clearRect(0, 0, this.overlay.width, this.overlay.height);
            this.overlayCC.drawImage(this.vid, 0, 0, this.overlay.width, this.overlay.height)
            var snapData = this.overlay.toDataURL('image/png');
            var imgSrc = 'data:image/png;' + snapData;
            // 符合张嘴的那一张
            if (this.getPhotoNum == 1) {
                // 识别出的第一张人脸
                this.firstfaceImg = imgSrc
            } else {
                document.querySelector('#imgSrc').setAttribute('src', imgSrc)
                this.sendImg(this.firstfaceImg)
            }
        },
        // 重置参数
        resetFun() {
            this.last_nose_distance_y = 0;
            this.last_nose_distance_x = 0;
            this.last_mouth_distance = 0;
            this.last_nose_left = 0;
            this.last_nose_top = 0;
            this.last_dis_eye_norse = 0;
            this.lastTime = 0;
            this.isTwinkle = false;
            this.isOpenMouth = false;
            this.first_outline_l = [];
            this.first_outline_r = [];
            this.mouthNum = 0;
            this.firstfaceImg = '';
            this.imgSrc = '';
            document.querySelector('#tipTxt').innerHTML = '请确保正对手机，光线充足'
            document.querySelector('#imgSrc').setAttribute('src', '')
            this.getPhotoNum = 0;
            $('.photoBox').children().show()
            this.onPlay();
        },
        // 调用接口发送截图
        sendImg(base64Img) {
			window.parent.postMessage(base64Img,'*')
            window.clearTimeout();
            setTimeout(() => {
                this.resetFun()
            }, 3000)
            // $.post('/sendImg', {
            //     imgSrc: base64Img
            // }, (result) => {
            //     // 这里根据后端校验返回结果做不同的处理结果
            //     if (result.code == '100') {
            //         // 验证不通过
            //         this.setHoverTxt('验证不通过');
            //         // $('#hoverTxt').html('验证不通过');
            //         this.resetFun()
            //     }
            // },()=>{
            // })

        },
        setHoverTxt(val) {
            $('#hoverTxt').removeClass('active');
            if (val) {
                $('#hoverTxt').addClass('active');
            } else {
                $('#hoverTxt').removeClass('active');
            }
            $('#hoverTxt').html(val)
        }
    };
    $('.warpper .btnBox img').click(function(){
        $('.app-container').removeClass('active')
        userface.resetFun();
    })
    userface.callTheCamera(userface.initFun);
</script>

</body>

</html>